#include <QFile>
#include <QCoreApplication>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <QSettings>
#include "kuploadmessage.h"

enum UploadMessageReturnState {
    OK = 0,
    InvalidArgumentFormat = 1,
    InvalidTid = 2,
    InvalidUploadedMessageSha256 = 3,
    InvalidUploadedMessageSha256Decryption = 4,
    InvalidCreateTimeStamp = 5
};

KUploadMessage::KUploadMessage(int ppid, QObject *parent):
    QObject(parent)
{
    qDebug() << "ppid = " << ppid << "getProgramPid" << getProgramPid("ukui-menu");

    /*if (ppid == getProgramPid("ukui-menu") || getProgramPid("ukui-sidebar") == ppid)*/ {
        g_flag = true;
    }
}

void KUploadMessage::initUploadInterface(QString packageName, QString messageType, QString packageInfoIdFolderPath)
{
    if (!g_flag) {
        return;
    }

    g_packageName = packageName;
    g_messageType = messageType;
    g_packageInfoIdFolderPath = packageInfoIdFolderPath;
    qDebug() << "m_packageName" << packageName << "m_messageType" << messageType << "m_packageInfoIdFolderPath" << packageInfoIdFolderPath;

    if (QDBusConnection::systemBus().isConnected()) {
        g_dbusInterface = new QDBusInterface(s_serverName, s_objectPath, s_interfaceName, QDBusConnection::systemBus(), this);
    }
}

int KUploadMessage::UploadMessage(const QString &uploadedMessage)
{
    if (!g_flag) {
        return -1;
    }

    qDebug() << "enter uploadMessage";
    UploadMessageReturnState uploadMessageReturnState;
    QString tid;
    QString nowTid;

    if (g_dbusInterface == nullptr) {
        return -1;
    }

    //  从配置文件中获取tid
    qDebug() << "get tid";
    checkDir();
    QSettings packageInfoIdSetting(g_packageInfoIdFolderPath + s_uploadMessageConf, QSettings::Format::IniFormat);
    packageInfoIdSetting.beginGroup(s_uploadMessageConf);

    if (packageInfoIdSetting.contains(s_tid)) {
        tid = packageInfoIdSetting.value(s_tid).toString();
    }

    packageInfoIdSetting.endGroup();
    // 生成packageInfo
    qDebug() << "create packageInfo";
    QJsonObject packageInfoJsonObj;
    packageInfoJsonObj.insert(s_packageName, g_packageName);
    packageInfoJsonObj.insert(s_messageType, g_messageType);
    packageInfoJsonObj.insert(s_tid, tid);
    QString packageInfo(QJsonDocument(packageInfoJsonObj).toJson(QJsonDocument::JsonFormat::Compact));
    // 计算sha256
    qDebug() << "calculate uploadMessageSha256";
    QCryptographicHash cryHash(QCryptographicHash::Algorithm::Sha256);
    cryHash.addData(uploadedMessage.toUtf8());
    QByteArray uploadedMessageSha256 = cryHash.result();
    //	加密数据
    QByteArray enUploadedMessageSha256 = encrypt(uploadedMessageSha256, publicKey).toBase64();
    //	调用函数
    QDBusMessage dbusMessage = g_dbusInterface->call(s_uploadMessageMethod, packageInfo, uploadedMessage, QString(enUploadedMessageSha256));

    //	获取返回值
    if (dbusMessage.type() == QDBusMessage::MessageType::ReplyMessage) {
        if (!dbusMessage.arguments().isEmpty()) {
            QList<QVariant> retValue = dbusMessage.arguments();

            if (retValue.size() == 2) {
                qDebug() << "dBusRet=" << retValue;
                uploadMessageReturnState = static_cast<UploadMessageReturnState>(retValue.first().toInt());
                nowTid =  retValue.last().toString();
            } else {
                qDebug() << "ret num error:" << retValue.size();
                return -1;
            }
        } else {
            qDebug() << "error Name:" << dbusMessage.errorName() << " errorMessage:" <<  dbusMessage.errorMessage();
            return -1;
        }
    } else {
        qDebug() << "dbusMessage type =" << dbusMessage.type();
        return -1;
    }

    //  检查返回值
    if (uploadMessageReturnState == UploadMessageReturnState::OK) {
        return 0;
    } else if (uploadMessageReturnState == UploadMessageReturnState::InvalidTid) {
        // 更新tid 更新tid  更新tid
        packageInfoIdSetting.beginGroup(s_uploadMessageConf);
        packageInfoIdSetting.setValue(s_tid, nowTid);
        packageInfoIdSetting.endGroup();
        return 0;
    } else {
        return -1;
    }
}

// 使用RSA公钥进行加密
QByteArray KUploadMessage::encrypt(const QByteArray &hasedValue, const QByteArray &keyBase64)
{
    BIO *bio = NULL;
    RSA *p_rsa = NULL;
    EVP_PKEY *key = NULL;
    EVP_PKEY_CTX *ctx = NULL;
    unsigned char *out;
    size_t outlen;
    QByteArray tempKey = QByteArray::fromBase64(keyBase64);

    if ((bio = BIO_new_mem_buf(tempKey.data(), tempKey.size())) == NULL) {
        evpError();
        return QByteArray();
    }

    if ((p_rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL)) == NULL) {
        evpError();
        BIO_free(bio);
        return QByteArray();
    }

    key = EVP_PKEY_new();

    if (key == NULL) {
        evpError();
        BIO_free(bio);
        RSA_free(p_rsa);
        return QByteArray();
    }

    if (EVP_PKEY_set1_RSA(key, p_rsa) <= 0) {
        evpError();
        BIO_free(bio);
        RSA_free(p_rsa);
        EVP_PKEY_free(key);
        return QByteArray();
    }

    ctx = EVP_PKEY_CTX_new(key, NULL);

    if (ctx == NULL) {
        evpError();
        BIO_free(bio);
        RSA_free(p_rsa);
        EVP_PKEY_free(key);
        return QByteArray();
    }

    if (EVP_PKEY_encrypt_init(ctx) <= 0) {
        evpError();
        BIO_free(bio);
        RSA_free(p_rsa);
        EVP_PKEY_free(key);
        EVP_PKEY_CTX_free(ctx);
        return QByteArray();
    }

    //  设置填充方式为OAEP
    if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
        evpError();
        BIO_free(bio);
        RSA_free(p_rsa);
        EVP_PKEY_free(key);
        EVP_PKEY_CTX_free(ctx);
        return QByteArray();
    }

    //  确定加密buf长度
    if (EVP_PKEY_encrypt(ctx, NULL, &outlen, (const unsigned char *)hasedValue.data(), hasedValue.size()) <= 0) {
        evpError();
        BIO_free(bio);
        RSA_free(p_rsa);
        EVP_PKEY_free(key);
        EVP_PKEY_CTX_free(ctx);
        return QByteArray();
    }

    out = (unsigned char *) OPENSSL_malloc(outlen);

    if (!out) {
        evpError();
        BIO_free(bio);
        RSA_free(p_rsa);
        EVP_PKEY_free(key);
        EVP_PKEY_CTX_free(ctx);
        return QByteArray();
    }

    //  进行加密
    if (EVP_PKEY_encrypt(ctx, out, &outlen, (const unsigned char *)hasedValue.data(), hasedValue.size()) <= 0) {
        evpError();
        BIO_free(bio);
        RSA_free(p_rsa);
        EVP_PKEY_free(key);
        EVP_PKEY_CTX_free(ctx);
        OPENSSL_free(out);
        return QByteArray();
    }

    QByteArray retByteArray((const char *)out, outlen);
    OPENSSL_free(out);
    BIO_free(bio);
    RSA_free(p_rsa);
    EVP_PKEY_free(key);
    EVP_PKEY_CTX_free(ctx);
    return retByteArray;
}

//  打印错误信息
void KUploadMessage::evpError(void)
{
    int n  = ERR_get_error();
    char szErr[512];
    char errStr[1024];
    ERR_error_string(n, szErr);
    sprintf(errStr, "error code = %d,code string = %s", n, szErr);
    qWarning() << errStr;
}

void KUploadMessage::checkDir()
{
    QDir tempDir(g_packageInfoIdFolderPath);

    if (!tempDir.exists()) {
        tempDir.mkpath(g_packageInfoIdFolderPath);
    }
}

int KUploadMessage::getProgramPid(const QString &programName)
{
    QProcess *processPid = new QProcess;
    processPid->start(QString("pgrep -f %1").arg(programName));
    QObject::connect(processPid, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), [ = ]() {
        processPid->deleteLater();
    });
    processPid->waitForFinished();
    int pidInfo = processPid->readAll().toInt();
    return pidInfo;
}
